this patch is to debug and fix an issue in mc (midnight commander)
whereby sometimes the subshell output was truncated and if small enough
completely unshown.
The fix handles the case when the order of two different inputs
(shell output and the pwd from PROMPT_COMMAND) don't arrive in that order.
This happens sometimes and this is why the truncation happened.
Also note that shell being STOP-ped can also happen in any order with the above two.
Patch by EmanueL Czirai.

reported issue: https://github.com/MidnightCommander/mc/issues/44#issuecomment-60467020

diff --git a/src/subshell.c b/src/subshell.c
index a85b258..5430f64 100644
--- a/src/subshell.c
+++ b/src/subshell.c
@@ -26,6 +26,12 @@
 /** \file subshell.c
  *  \brief Source: concurrent shell support
  */
+/*
+#define DEBUGY
+#define DEBUGY2
+#define DEBUGY3
+#define DEBUGY4
+*/
 
 #include <config.h>
 
@@ -173,11 +179,23 @@ write_all (int fd, const void *buf, size_t count)
 {
     ssize_t written = 0;
 
+#ifdef DEBUGY
+    fprintf(stderr,"write_all(%d)\n\r",count);
+    fflush(stderr);
+#endif
+    if (count == 11) {
+        //      fprintf(stderr,"%s",NULL); //#emacs
+    }
     while (count > 0)
     {
         ssize_t ret;
 
         ret = write (fd, (const unsigned char *) buf + written, count);
+#ifdef DEBUGY
+        //        fprintf(stderr,"written %d/%d/%d\n",ret,written,count);
+        //        fflush(stderr);
+        //fsync(fd);
+#endif
         if (ret < 0)
         {
             if (errno == EINTR)
@@ -443,7 +461,14 @@ synchronize (void)
     if (subshell_state != ACTIVE)
     {
         /* Discard all remaining data from stdin to the subshell */
-        tcflush (subshell_pty_slave, TCIFLUSH);
+        tcflush (subshell_pty_slave, TCIFLUSH);//no effect when commented out
+        /*        tcdrain(subshell_pty_slave);
+                  tcdrain(mc_global.tty.subshell_pty);*/
+#ifdef DEBUGY
+        fprintf(stderr,"drain\n\r");
+        fflush(stderr);
+#endif
+        //        tcflush (subshell_pty_slave, TCOFLUSH);
     }
 
     subshell_stopped = FALSE;
@@ -464,37 +489,87 @@ feed_subshell (int how, int fail_on_error)
     int i;                      /* Loop counter */
 
     struct timeval wtime;       /* Maximum time we wait for the subshell */
-    struct timeval *wptr;
-
-    /* we wait up to 10 seconds if fail_on_error, forever otherwise */
-    wtime.tv_sec = 10;
-    wtime.tv_usec = 0;
-    wptr = fail_on_error ? &wtime : NULL;
 
-    while (TRUE)
+#ifdef DEBUGY
+    int moe=0;
+#endif
+    int gotpwdoutput=FALSE;//did we get the pwd output? (search for "sleep 1" in this source code)
+    /* waiting for this won't work when ie. ls / -R
+       while (!subshell_stopped) {//even waiting for the subshell to STOP still doesn't guarantee order, so read2 still happens before read1, sometimes; and exiting when read2(the pwd from prompt) happens will discard read1(shell output)
+#ifdef DEBUGY4
+fprintf(stderr,"here0 WAITING subshell_stopped=%d subshell_ready=%d\n\r",subshell_stopped,subshell_ready);
+fflush(stderr);
+#endif
+}*/
+    //XXX:shell output, output of pwd to the pipe AND shell being STOPped can all happen in any order!
+    //XXX: basically, I need to know that shell output and subshell_pipe[READ] are both drained before exiting this function; but because they don't happen in order(well they do most of the time, but sometimes they don't - and that's when I'm losing output which is unacceptable), I can't just exit when subshell_pipe[READ] input is received. And can't use the fact that I know when shell is STOPped because there's still data to be read even after that happens. Also can't let select block because it will block  and we can't afford to wait for the timeout.
+while (
+#ifdef DEBUGY
+    ++moe
+#else
+    TRUE
+#endif
+    )// && !subshell_stopped)
 {
     int maxfdp;
 
+
+    //if read2 happens before read1 on mc startup, then it will wait 10 sec...
+    //if the second timeout below is 0 then mc will use 100% cpu because select won't block
+    /* we wait up to 10 seconds if fail_on_error, forever otherwise */
+    wtime.tv_sec = (fail_on_error && !gotpwdoutput) ? 10:0;//10 sec timeout if fail_on_error, or no wait otherwise;
+    //100 iterations per second(in this while) because we can't rely on subshell_stopped to be true before we consume all outputs
+    wtime.tv_usec = 10000;//add some delay to avoid high cpu usage during mc startup and executing of commands while subshell isn't active
+
+#ifdef DEBUGY
+    fprintf(stderr,"here1 while=%d subshell_stopped=%d subshell_ready=%d subshell_state=%d\n\r",moe,subshell_stopped,subshell_ready,subshell_state);
+    fflush(stderr);
+#endif
     if (!subshell_alive)
         return FALSE;
+#ifdef DEBUGY3
+    fprintf(stderr,"here2 pty=%d\n\r",mc_global.tty.subshell_pty); //reached
+    fflush(stderr);
+#endif
 
     /* Prepare the file-descriptor set and call 'select' */
 
+    /*        fsync(mc_global.tty.subshell_pty);//no effect
+              tcdrain(subshell_pty_slave);
+              tcdrain(mc_global.tty.subshell_pty);*/
     FD_ZERO (&read_set);
-        FD_SET (mc_global.tty.subshell_pty, &read_set);
-        FD_SET (subshell_pipe[READ], &read_set);
+    FD_SET (mc_global.tty.subshell_pty, &read_set);//from this read1 reads echoes command and output of that command
+    FD_SET (subshell_pipe[READ], &read_set); //from this read2 reads pwd, apparently
     maxfdp = max (mc_global.tty.subshell_pty, subshell_pipe[READ]);
+    //        maxfdp = max (maxfdp, subshell_pipe[WRITE]); no effect
     if (how == VISIBLY)
     {
         FD_SET (STDIN_FILENO, &read_set);
         maxfdp = max (maxfdp, STDIN_FILENO);
     }
 
-        if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1)
-        {
+#ifdef DEBUGY3
+    fprintf(stderr,"before select %ld.%06ld\n\r",wtime.tv_sec,wtime.tv_usec);
+    fflush(stderr);
+#endif
+    //make select have no timeout when subshell is active, otherwise use &wtime
+    //also wait indefinitely when in the process of executing a command(RUNNING_COMMAND) (ie. !subshell_stopped) XXX: actually can't use !subshell_stopped here because it will lockup sometimes, can't rely on it to be set to 1 before all the output(pwd and shell's) is received and processed. So basically we can't know when RUNNING_COMMAND is done executing it, at least not before we're done reading outputs.
+    //need to use indefinite timeout here only when subshell_ready, because otherwise it would lockup waiting; in theory if subshell_stopped were reliable I could use it to know when to not use indefinite timeout, but that doesn't work because sometimes it will lockup, so we're stuck having to use iterations when !subshell_ready with ACTIVE subshell ie. editing something with vim (even when just RUNNING_COMMAND)
+    //XXX: don't use !subshell_stopped because it will lockup when ie. keep enter pressed on a directory in a mc panel, or keep Tab pressed.
+    if (select (maxfdp + 1, &read_set, NULL, NULL, ( (ACTIVE == subshell_state && subshell_ready) ? NULL : &wtime)  ) == -1) 
+    {
+#ifdef DEBUGY
+        fprintf(stderr,"here3\n\r"); //not reached
+        //reached only when disabling that read2 return TRUE below
+        fflush(stderr);
+#endif
         /* Despite using SA_RESTART, we still have to check for this */
         if (errno == EINTR)
-            {
+        {//this is supposed to trigger at next PROMPT via PROMPT_COMMAND doing a kill -STOP $$ however it doesn't always happen and it leaves you at prompt so on next prompt from there(ie. press Enter once) it happens; and looks like sometimes even blocks on mc startup! so this is not the way; maybe if we move synchronize() here... nope, still locks up on startup(but this time 10 sec timeout until disable subshell happens)
+#ifdef DEBUGY
+            fprintf(stderr,"hereEINTR\n\r");//not currently reached
+            fflush(stderr);
+#endif
             if (mc_global.tty.winch_flag != 0)
                 tty_change_screen_size ();
 
@@ -505,6 +580,11 @@ feed_subshell (int how, int fail_on_error)
                 unix_error_string (errno));
         exit (EXIT_FAILURE);
     }
+#ifdef DEBUGY3
+    fprintf(stderr,"after select %ld.%06ld\n\r",wtime.tv_sec,wtime.tv_usec);
+    fprintf(stderr,"here4  maxfdp=%d how=%d pipe[r]=%d pipe[w]=%d\n\r",maxfdp,how,subshell_pipe[READ],subshell_pipe[WRITE]); //reached
+    fflush(stderr);
+#endif
 
     if (FD_ISSET (mc_global.tty.subshell_pty, &read_set))
         /* Read from the subshell, write to stdout */
@@ -512,10 +592,14 @@ feed_subshell (int how, int fail_on_error)
         /* This loop improves performance by reducing context switches
            by a factor of 20 or so... unfortunately, it also hangs MC
            randomly, because of an apparent Linux bug.  Investigate. */
-            /* for (i=0; i<5; ++i)  * FIXME -- experimental */
+        //for (i=0; i<5; ++i)  /* FIXME -- experimental */ //this is a bad idea, avoid that, it'll obviously lockup by read-blocking #emacs
     {
         bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
 
+#ifdef DEBUGY2
+        fprintf(stderr,"\e[1;32mread1\e[0m: %d\n\r",bytes); //#emacs
+        fflush(stderr);
+#endif
         /* The subshell has died */
         if (bytes == -1 && errno == EIO && !subshell_alive)
             return FALSE;
@@ -527,6 +611,11 @@ feed_subshell (int how, int fail_on_error)
             exit (EXIT_FAILURE);
         }
 
+#ifdef DEBUGY
+        write_all (STDERR_FILENO, pty_buffer, bytes);
+        fflush(stderr);
+#endif
+
         if (how == VISIBLY)
             write_all (STDOUT_FILENO, pty_buffer, bytes);
     }
@@ -535,6 +624,13 @@ feed_subshell (int how, int fail_on_error)
         /* Read the subshell's CWD and capture its prompt */
     {
         bytes = read (subshell_pipe[READ], subshell_cwd, MC_MAXPATHLEN + 1);
+#ifdef DEBUGY2
+        fprintf(stderr,"\e[1;31mread2\e[0m: %d pty=%d\n\r",bytes,mc_global.tty.subshell_pty); //#emacs
+#ifdef DEBUGY
+        write_all (STDERR_FILENO, subshell_cwd, bytes);
+#endif
+        fflush(stderr);
+#endif
         if (bytes <= 0)
         {
             tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
@@ -545,20 +641,17 @@ feed_subshell (int how, int fail_on_error)
 
         subshell_cwd[bytes - 1] = 0;        /* Squash the final '\n' */
 
-            synchronize ();
-
-            subshell_ready = TRUE;
-            if (subshell_state == RUNNING_COMMAND)
-            {
-                subshell_state = INACTIVE;
-                return TRUE;
-            }
+        gotpwdoutput=TRUE;
     }
 
     else if (FD_ISSET (STDIN_FILENO, &read_set))
         /* Read from stdin, write to the subshell */
     {
         bytes = read (STDIN_FILENO, pty_buffer, sizeof (pty_buffer));
+#ifdef DEBUGY
+        fprintf(stderr,"read3: %d\n\r",bytes); //#emacs
+        fflush(stderr);
+#endif
         if (bytes <= 0)
         {
             tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
@@ -573,6 +666,11 @@ feed_subshell (int how, int fail_on_error)
                 write_all (mc_global.tty.subshell_pty, pty_buffer, i);
                 if (subshell_ready)
                     subshell_state = INACTIVE;
+
+#ifdef DEBUGY
+                fprintf(stderr,"\e[1;33m!!\e[0mreturn TRUE due to C-O subshell_ready=%d\n\r",subshell_ready); //#emacs
+                fflush(stderr);
+#endif
                 return TRUE;
             }
 
@@ -581,9 +679,59 @@ feed_subshell (int how, int fail_on_error)
         if (pty_buffer[bytes - 1] == '\n' || pty_buffer[bytes - 1] == '\r')
             subshell_ready = FALSE;
     }
-        else
+    else {
+        if (fail_on_error) {
+            if (subshell_stopped && gotpwdoutput && subshell_state==RUNNING_COMMAND) {
+                break;//handle the lockup on mc startup
+            }else{
+#ifdef DEBUGY
+                fprintf(stderr,"\e[1;33m!!\e[0mreturning FALSE fail_on_error=%d subshell_ready=%d\n\r",fail_on_error, subshell_ready); //#emacs
+                fflush(stderr);
+#endif
+                //this will lead to disabling subshell
                 return FALSE;
             }
+        }else {
+            //only exit when subshell is stopped
+            if (subshell_stopped) {
+                if (subshell_state == ACTIVE) {
+#ifdef DEBUGY
+                    fprintf(stderr,"reactivate subshell from stop subshell_stopped=%d subshell_ready=%d\n\r",subshell_stopped,subshell_ready); //#emacs
+                    fflush(stderr);
+#endif
+                    synchronize ();
+                    subshell_ready = TRUE;
+#ifdef DEBUGY
+                    fprintf(stderr,"reactivateD subshell from stop subshell_stopped=%d subshell_ready=%d\n\r",subshell_stopped,subshell_ready); //#emacs
+                    fflush(stderr);
+#endif
+                } else {
+#ifdef DEBUGY
+                    fprintf(stderr,"\e[1;33m!!\e[0mBREAK fail_on_error=%d subshell_ready=%d subshell_state=%d\n\r",fail_on_error, subshell_ready,subshell_state); //#emacs
+                    fflush(stderr);
+#endif
+                    break;//to mark shell as not still executing a command
+                }
+            }//otherwise keep trying to get input (via that select)
+        }
+    }
+}//while
+
+#ifdef DEBUGY
+fprintf(stderr,"\e[1;33m!!\e[0mout of while, before sync, subshell_stopped=%d subshell_ready=%d subshell_state=%d\n\r",subshell_stopped,subshell_ready,subshell_state); //#emacs
+fflush(stderr);
+#endif
+synchronize ();
+subshell_ready = TRUE;
+if (subshell_state == RUNNING_COMMAND)
+{
+    subshell_state = INACTIVE;
+#ifdef DEBUGY
+    fprintf(stderr,"\e[1;33m!!\e[0mreturn TRUE subshell_stopped=%d subshell_ready=%d\n\r",subshell_stopped,subshell_ready); //#emacs
+    fflush(stderr);
+#endif
+}
+return TRUE;
 }
 
 /* --------------------------------------------------------------------------------------------- */
@@ -760,6 +908,7 @@ init_subshell (void)
     /* This must be remembered across calls to init_subshell() */
     static char pty_name[BUF_SMALL];
     char precmd[BUF_MEDIUM];
+    //    fprintf (stderr, "init_subshell()\r\n");
 
     switch (check_sid ())
     {
@@ -873,8 +1022,16 @@ init_subshell (void)
     switch (subshell_type)
     {
         case BASH:
-        g_snprintf (precmd, sizeof (precmd),
+            g_snprintf (precmd, sizeof (precmd), //#emacs
                     " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]);
+            //doesn't work with echo:
+            //                    " PROMPT_COMMAND='echo 1;pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]);
+            //works with sleep 1 before pwd:
+            //                    " PROMPT_COMMAND='sleep 1;pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]);
+            //the noflush issue is reproducible 100% of the time with sleep 1 before kill, and after pwd:
+            //                    " PROMPT_COMMAND='pwd>&%d;sleep 1;kill -STOP $$'\n", subshell_pipe[WRITE]);
+            //                    " PROMPT_COMMAND='sleep 1;pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]);
+            //                    " PROMPT_COMMAND='echo '/'>&%d;sleep 1;kill -STOP $$'\n", subshell_pipe[WRITE]);
             break;
 
         case ZSH:
@@ -906,6 +1063,8 @@ init_subshell (void)
     tty_enable_interrupt_key ();
     if (!feed_subshell (QUIETLY, TRUE))
     {
+        fprintf(stderr,"disabling subshell\n\r");
+        fflush(stderr);
         mc_global.tty.use_subshell = FALSE;
     }
     tty_disable_interrupt_key ();
@@ -947,6 +1106,10 @@ invoke_subshell (const char *command, int how, vfs_path_t ** new_dir_vpath)
         subshell_ready = FALSE;
     }
 
+#ifdef DEBUGY
+    fprintf(stderr,"hereNOW1\n\r");
+    fflush(stderr);
+#endif
     feed_subshell (how, FALSE);
 
     if (new_dir_vpath != NULL && subshell_alive)
